package org.go.trigger;

import java.io.Serializable;
import java.util.Comparator;
import java.util.Date;
import java.util.TimeZone;

import org.go.scheduler.ScheduleBuilder;
import org.go.scheduler.Scheduler;
import org.go.work.WorkDataMap;
import org.go.work.WorkKey;

/**
 * 
 * @author hejie
 *
 */
public interface Trigger extends Serializable, Cloneable, Comparable<Trigger> {
	/**
	 * A Comparator that compares trigger's next fire times, or in other words,
	 * sorts them according to earliest next fire time.  If the fire times are
	 * the same, then the triggers are sorted according to priority (highest
	 * value first), if the priorities are the same, then they are sorted
	 * by key.
	 */
	public class TriggerTimeComparator implements Comparator<Trigger>, Serializable {
		/**
		 * 
		 */
		private static final long serialVersionUID = -3358753273825050849L;

		@Override
		public int compare(Trigger trig1, Trigger trig2) {
			Date t1 = trig1.getNextFireTime();
			Date t2 = trig2.getNextFireTime();

			if (t1 != null || t2 != null) {
				if (t1 == null) {
					return 1;
				}

				if (t2 == null) {
					return -1;
				}

				if (t1.before(t2)) {
					return -1;
				}

				if (t1.after(t2)) {
					return 1;
				}
			}

			int comp = trig2.getPriority() - trig1.getPriority();
			if (comp != 0) {
				return comp;
			}

			return trig1.getTriggerKey().compareTo(trig2.getTriggerKey());
		}
	};

	/**
	 * The default value for priority.
	 */
	public static final int DEFAULT_PRIORITY = 5;

	/**
	 * Instructs the <code>{@link Scheduler}</code> that the 
	 * <code>Trigger</code> will never be evaluated for a misfire situation, 
	 * and that the scheduler will simply try to fire it as soon as it can, 
	 * and then update the Trigger as if it had fired at the proper time. 
	 * 
	 * <p>NOTE: if a trigger uses this instruction, and it has missed 
	 * several of its scheduled firings, then 
	 */
	public static final int MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY = -1;

	/**
	 * Instructs the <code>{@link Scheduler}</code> that upon a mis-fire
	 * situation, the <code>updateAfterMisfire()</code> method will be called
	 * on the <code>Trigger</code> to determine the mis-fire instruction,
	 * which logic will be trigger-implementation-dependent.
	 * 
	 * <p>
	 * In order to see if this instruction fits your needs, you should look at
	 * the documentation for the <code>getSmartMisfirePolicy()</code> method
	 * on the particular <code>Trigger</code> implementation you are using.
	 * </p>
	 */
	public static final int MISFIRE_INSTRUCTION_SMART_POLICY = 0;

	public static final long serialVersionUID = -3904243490805975570L;

	Trigger clone();

	Date computeFirstFireTime(Calendar calendar);

	/**
	 * Get the name of the <code>{@link Calendar}</code> associated with this
	 * Trigger.
	 * 
	 * @return <code>null</code> if there is no associated Calendar.
	 */
	String getCalendarName();

	String getCronExpression();

	/**
	 * Return the description given to the <code>Trigger</code> instance by
	 * its creator (if any).
	 * 
	 * @return null if no description was set.
	 */
	String getDescription();

	/**
	 * Get the time at which the <code>Trigger</code> should quit repeating -
	 * regardless of any remaining repeats (based on the trigger's particular 
	 * repeat settings). 
	 * 
	 * @see #getFinalFireTime()
	 */
	Date getEndTime();

	String getExpressionSummary();

	/**
	 * Returns the last time at which the <code>Trigger</code> will fire, if
	 * the Trigger will repeat indefinitely, null will be returned.
	 * 
	 * <p>
	 * Note that the return time *may* be in the past.
	 * </p>
	 */
	Date getFinalFireTime();

	String getFireInstanceId();

	/**
	 * Returns the next time at which the <code>Trigger</code> will fire,
	 * after the given time. If the trigger will not fire after the given time,
	 * <code>null</code> will be returned.
	 */
	Date getFireTimeAfter(Date afterTime);

	long getInterval();

	/**
	 * Get the <code>JobDataMap</code> that is associated with the 
	 * <code>Trigger</code>.
	 * 
	 * <p>
	 * Changes made to this map during job execution are not re-persisted, and
	 * in fact typically result in an <code>IllegalStateException</code>.
	 * </p>
	 */
	WorkDataMap getJobDataMap();

	WorkKey getJobKey();

	TriggerKey getKey();

	/**
	 * Get the instruction the <code>Scheduler</code> should be given for
	 * handling misfire situations for this <code>Trigger</code>- the
	 * concrete <code>Trigger</code> type that you are using will have
	 * defined a set of additional <code>MISFIRE_INSTRUCTION_XXX</code>
	 * constants that may be set as this property's value.
	 * 
	 * <p>
	 * If not explicitly set, the default value is <code>MISFIRE_INSTRUCTION_SMART_POLICY</code>.
	 * </p>
	 * 
	 * @see #MISFIRE_INSTRUCTION_SMART_POLICY
	 * @see #updateAfterMisfire(Calendar)
	 * @see SimpleTrigger
	 * @see CronTrigger
	 */
	int getMisfireInstruction();

	/**
	 * Returns the next time at which the <code>Trigger</code> is scheduled to fire. If
	 * the trigger will not fire again, <code>null</code> will be returned.  Note that
	 * the time returned can possibly be in the past, if the time that was computed
	 * for the trigger to next fire has already arrived, but the scheduler has not yet
	 * been able to fire the trigger (which would likely be due to lack of resources
	 * e.g. threads).
	 *
	 * <p>The value returned is not guaranteed to be valid until after the <code>Trigger</code>
	 * has been added to the scheduler.
	 * </p>
	 *
	 * @see TriggerUtils#computeFireTimesBetween(Trigger, Calendar, Date, Date)
	 */
	Date getNextFireTime();

	/**
	 * Returns the previous time at which the <code>Trigger</code> fired.
	 * If the trigger has not yet fired, <code>null</code> will be returned.
	 */
	Date getPreviousFireTime();

	/**
	 * The priority of a <code>Trigger</code> acts as a tiebreaker such that if 
	 * two <code>Trigger</code>s have the same scheduled fire time, then the
	 * one with the higher priority will get first access to a worker
	 * thread.
	 * 
	 * <p>
	 * If not explicitly set, the default value is <code>5</code>.
	 * </p>
	 * 
	 * @see #DEFAULT_PRIORITY
	 */
	int getPriority();

	int getRepeatCount();

	char[] getRepeatInterval();

	/**
	 * Get a {@link ScheduleBuilder} that is configured to produce a 
	 * schedule identical to this trigger's schedule.
	 * 
	 * @see #getTriggerBuilder()
	 */
	ScheduleBuilder<? extends Trigger> getScheduleBuilder();

	/**
	 * Get the time at which the <code>Trigger</code> should occur.
	 */
	Date getStartTime();

	int getTimesTriggered();

	TimeZone getTimeZone();

	/**
	 * Get a {@link TriggerBuilder} that is configured to produce a 
	 * <code>Trigger</code> identical to this one.
	 * 
	 * @see #getScheduleBuilder()
	 */
	TriggerBuilder<? extends Trigger> getTriggerBuilder();

	/**
	 * 
	 * @return
	 */
	String getTriggerKey();

	/**
	 * 
	 * @return
	 */
	String getType();

	boolean hasAdditionalProperties();

	/**
	 * Used by the <code>{@link Scheduler}</code> to determine whether or not
	 * it is possible for this <code>Trigger</code> to fire again.
	 * 
	 * <p>
	 * If the returned value is <code>false</code> then the <code>Scheduler</code>
	 * may remove the <code>Trigger</code> from the <code>{@link org.quartz.spi.JobStore}</code>.
	 * </p>
	 */
	boolean mayFireAgain();

	void setCalendarName(String calendarName);

	void setDescription(String description);

	void setEndTime(Date endTime);

	void setFireInstanceId(String firedTriggerRecordId);

	void setInterval(long interval);

	void setMisfireInstruction(int misFireInstr);

	/**
	 * 
	 * @param nft
	 */
	void setNextFireTime(Date nft);

	/**
	 * 
	 * @param pft
	 */
	void setPreviousFireTime(Date pft);

	/**
	 * 
	 * @param priority
	 */
	void setPriority(int priority);

	/**
	 * Sets the start time.
	 *
	 * @param startTime the new start time
	 */
	void setStartTime(Date startTime);

	/**
	 * 
	 * @param key
	 */
	void setTriggerKey(TriggerKey key);

	/**
	 * Triggered.
	 *
	 * @param cal the cal
	 */
	void triggered(Calendar cal);

	/**
	 * 
	 * @param cal
	 */
	void updateAfterMisfire(Calendar cal);

	void updateWithNewCalendar(Calendar calendar, long misfireThreshold);

	boolean validateMisfireInstruction(int misfireInstruction);

	/**
	 * <p>
	 * Compare the next fire time of this <code>Trigger</code> to that of
	 * another by comparing their keys, or in other words, sorts them
	 * according to the natural (i.e. alphabetical) order of their keys.
	 * </p>
	 */
	@Override
	public int compareTo(Trigger other);

	/**
	 * Trigger equality is based upon the equality of the TriggerKey.
	 * 
	 * @return true if the key of this Trigger equals that of the given Trigger.
	 */
	@Override
	public boolean equals(Object other);
}
